package github.flandre.examplemod.common.energy.tileentity;

import com.google.common.collect.Queues;
import com.mojang.datafixers.DSL;
import github.flandre.examplemod.common.energy.block.FEDemoGeneratorBlock;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.world.LightType;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ObjectHolder;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.util.Objects;
import java.util.Queue;

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class FEDemoGeneratorTileEntity extends TileEntity implements ITickableTileEntity
{
    public static final String NAME = "examplemod:generator";

    @ObjectHolder(NAME)
    public static TileEntityType<FEDemoGeneratorTileEntity> TILE_ENTITY_TYPE;
    private int energy = 0;
    private final int MaxEnergy = 192_000;

    @SubscribeEvent
    public static void onRegisterTileEntityType(@Nonnull RegistryEvent.Register<TileEntityType<?>> event)
    {
        event.getRegistry().register(TileEntityType.Builder.create(FEDemoGeneratorTileEntity::new, FEDemoGeneratorBlock.BLOCK)
                .build(DSL.remainderType()).setRegistryName(NAME));
    }

    private FEDemoGeneratorTileEntity()
    {
        super(TILE_ENTITY_TYPE);
    }

    @Override
    public void read(@NotNull BlockState state, CompoundNBT compound) {
        this.energy = compound.getInt("GeneratorEnergy");
        super.read(state, compound);
    }


    @Nonnull
    @Override
    public CompoundNBT write(@Nonnull CompoundNBT compound)
    {
        compound.putInt("GeneratorEnergy", this.energy);
        return super.write(compound);
    }

    private final LazyOptional<IEnergyStorage> lazyOptional = LazyOptional.of(() -> new IEnergyStorage()
    {
        @Override
        public int receiveEnergy(int maxReceive, boolean simulate)
        {
            return 0;
        }

        @Override
        public int extractEnergy(int maxExtract, boolean simulate)
        {
            int energy = this.getEnergyStored();
            int diff = Math.min(energy, maxExtract);
            if (!simulate)
            {
                FEDemoGeneratorTileEntity.this.energy -= diff;
                if (diff != 0)
                {
                    FEDemoGeneratorTileEntity.this.markDirty();
                }
            }
            return diff;
        }

        @Override
        public int getEnergyStored()
        {
            return Math.max(0, Math.min(this.getMaxEnergyStored(), FEDemoGeneratorTileEntity.this.energy));
        }

        @Override
        public int getMaxEnergyStored()
        {
            return MaxEnergy;
        }

        @Override
        public boolean canExtract()
        {
            return true;
        }

        @Override
        public boolean canReceive()
        {
            return false;
        }
    });

    @Nonnull
    @Override
    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, Direction side)
    {
        boolean isEnergy = Objects.equals(cap, CapabilityEnergy.ENERGY) && side.getAxis().isHorizontal();
        return isEnergy ? this.lazyOptional.cast() : super.getCapability(cap, side);
    }

    //机器每tick都会运行的函数
    //我们既然希望方块收集太阳能，那我们自然是希望方块实体所存储的能量随时间递增。这需要我们让我们的方块实体每 tick 执行一段代码，原版 Minecraft 为我们提供了 ITickableTileEntity 接口。我们只需要让我们的类在继承 TileEntity 的同时实现这一接口即可：
    @Override
    public void tick() {
        if (this.world != null && !this.world.isRemote)
        {
            this.generateEnergy(this.world);
            this.transferEnergy(this.world);
        }
    }


    private void generateEnergy(@Nonnull World world)
    {
        if (world.getDimensionType().hasSkyLight())
        {
            int light = world.getLightFor(LightType.SKY, this.pos.up()) - world.getSkylightSubtracted(); //获取光亮
            int diff = Math.min(MaxEnergy - this.energy, 10 * Math.max(0, light - 10));
            if (diff != 0)
            {
                this.energy += diff;
                this.markDirty();
            }
        }
    }

    private final Queue<Direction> directionQueue = Queues.newArrayDeque(Direction.Plane.HORIZONTAL); //水平面的方向加入队列
    private void transferEnergy(@Nonnull World world)
    {
        this.directionQueue.offer(this.directionQueue.remove()); //添加一个 删除一个 为了雨露均沾的发送电力
        //通过遍历水平方向的所有相邻方块，然后逐个注入能量，一次最多注入 500 FE。
        // 注意在获取相邻方块时，需要获取的是相反的方向（例如对于东侧的方块，
        // 注入能量时应该从该方块的西侧注入），也就是对 Direction 调用 getOpposite 方法并取其返回值。
        for (Direction direction : this.directionQueue)
        {
            TileEntity tileEntity = world.getTileEntity(this.pos.offset(direction));
            if (tileEntity != null)
            {
                tileEntity.getCapability(CapabilityEnergy.ENERGY, direction.getOpposite()).ifPresent(e ->
                {
                    if (e.canReceive())
                    {
                        int diff = e.receiveEnergy(Math.min(500, this.energy), false);
                        if (diff != 0)
                        {
                            this.energy -= diff;
                            this.markDirty();
                        }
                    }
                });
            }
        }
    }


}